﻿using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Seagull.Configuration;
using Seagull.DAL.DriveManagers;
using Seagull.Events;
using Seagull.Events.Data;
using Seagull.Events.Drive;
using Seagull.Events.Job;
using Seagull.Generic;
using Seagull.Model;
using Seagull.Model.Alerts;
using Seagull.Shell.Properties;
using Seagull.Shell.Utils;
using Seagull.Shell.Views;
using Seagull.Shell.Windows;
using ThreadingTimer = System.Threading.Timer;

namespace Seagull.Shell
{
    public partial class Main : Form
    {
        private readonly Lazy<ChartView> _chartView;
        private readonly Lazy<DeployView> _deployView;
        private readonly Lazy<MapView> _mapView;
        /// <summary>
        /// 用于定时清除提示信息。
        /// </summary>
        private readonly ThreadingTimer _notificationTimer;
        private ConfirmClearRemovableDiskWindow _confirmClearWindow;
        private readonly Lazy<TrendView> _trendView;

        private bool _hasNoDataAlert = false;

        public Main()
        {
            InitializeComponent();
            Text = ConfigurationManager.AppSettings["WindowTitle"];

            _mapView = new Lazy<MapView>(() => RegisterView(new MapView()));
            _deployView = new Lazy<DeployView>(() => RegisterView(new DeployView()));
            _chartView = new Lazy<ChartView>(() => RegisterView(new ChartView()));
            _trendView = new Lazy<TrendView>(() => RegisterView(new TrendView()));
            ChangeView(ViewKind.Map);

            _notificationTimer = new ThreadingTimer(t =>
                this.BeginInvoke(() =>
                {
                    _notificationToolStripStatusLabel.Text = string.Empty;
                    _notificationToolStripStatusLabel.Invalidate();
                }));

            RegisterEvents();
        }

        private void RegisterEvents()
        {
            #region Alert & Notification Related Events

            AlertEvent.Instance.Register(OnAlert);
            LogOnlyAlertEvent.Instance.Register(OnAlert);
            NotificationEvent.Instance.Register(OnNotification);

            #endregion

            #region Time Related Events

            NewMinuteEvent.Instance.Register(OnNewMinute);

            #endregion

            #region Job Related Events

            JobStartedEvent.Instance.Register(OnJobStarted);
            JobProgressEvent.Instance.Register(OnJobProgress);
            JobEndedEvent.Instance.Register(OnJobEnded);

            #endregion

            #region Drive Related Events

            DriveArrivedEvent.Instance.Register(OnDriveArrived);
            DriveRemovedEvent.Instance.Register(OnDriveRemoved);
            LocalDriveSpaceChangedEvent.Instance.Register(OnLocalDriveSpaceChanged);
            RemovableDriveSpaceChangedEvent.Instance.Register(OnRemovableDriveSpaceChanged);

            #endregion
        }

        #region Close Window

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);

            // 如果是用户主动关闭窗口，则要求输入密码
            // 否则 （如系统关机、任务管理器）则不弹出密码框
            if(e.CloseReason == CloseReason.UserClosing && !PasswordCorrect())
            {
                // 取消关闭
                e.Cancel = true;
            }
        }

        /// <summary>
        /// 要求输入密码并判断是否正确
        /// </summary>
        /// <returns></returns>
        private static bool PasswordCorrect()
        {
            return new PasswordMsgBox(Config.Instance.UIConfig.UILockPassword).ShowDialog() == DialogResult.OK;
        }

        #endregion


        #region Drive Related

        private void OnDriveRemoved(DriveInfo drive)
        {
            this.BeginInvoke(() =>
            {
                if(_confirmClearWindow != null && drive.Name == _confirmClearWindow.Drive)
                {
                    _confirmClearWindow.Close();
                    _confirmClearWindow.Dispose();
                    _confirmClearWindow = null;
                }
            });
        }

        private void OnDriveArrived(DriveInfo drive)
        {
            this.BeginInvoke(() =>
            {
                if(GetNotAllowedDirs(drive.Name).Any() && _confirmClearWindow == null)
                {
                    ShowConfirmClearWindow(drive.Name);
                }
            });
        }

        private void OnLocalDriveSpaceChanged(int usedPercentage)
        {
            this.BeginInvoke(() =>
            {
                _localDiskSpaceIndicator.Value = usedPercentage;
                _localDiskSpaceIndicator.SetColor();
            });
        }

        private void OnRemovableDriveSpaceChanged(int obj)
        {
            this.BeginInvoke(() =>
            {
                _removableDiskSpaceIndicator.Value =
                    RemovableDriveManager.Instance.UsedSpacePercentage;
                _removableDiskSpaceIndicator.SetColor();
            });
        }

        private static DirectoryInfo[] GetNotAllowedDirs(string drive)
        {
            var allowedDirs =
                Config.Instance.PathConfig.AllowedFolderNamesOnRemovableDevice.Split(new[] { ',', ';', '|' },
                    StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()).ToArray();
            var notAllowedDirs =
                new DirectoryInfo(drive).EnumerateDirectories("*", SearchOption.TopDirectoryOnly).Where(
                    t => !allowedDirs.Contains(t.Name)).ToArray();
            return notAllowedDirs;
        }

        #region Confirm Clear Window

        private void ShowConfirmClearWindow(string drive)
        {
            _confirmClearWindow = new ConfirmClearRemovableDiskWindow
            {
                Drive = drive,
                DataFolderName = Config.Instance.PathConfig.AllowedFolderNamesOnRemovableDevice
            };
            _confirmClearWindow.ClearConfirmed += OnClearConfirmed;
            _confirmClearWindow.FormClosed += OnConfirmWindowClosed;
            _confirmClearWindow.Show();
        }

        private void OnConfirmWindowClosed(object sender, FormClosedEventArgs e)
        {
            _confirmClearWindow.Dispose();
            _confirmClearWindow = null;
        }

        private static void OnClearConfirmed(object sender, EventArgs<string> e)
        {
            foreach(var folder in GetNotAllowedDirs(e.Data))
            {
                folder.Delete(true);
            }
        }

        #endregion

        #endregion


        #region Job Related

        private CancellationTokenSource _cancellationSignal;

        private void ManuallyStartAllMaintananceJob()
        {
            if(_cancellationSignal == null)
            {
                StartJobEvent.Instance.Publish(Tuple.Create(JobKind.All, DateTime.MinValue));
            }
        }

        public void ManuallyStartStatJob()
        {
            StartJobEvent.Instance.Publish(Tuple.Create(JobKind.Statistic, DateTime.MinValue));
        }

        private void OnJobProgress(Tuple<JobKind, int, CancellationTokenSource> jobProgress)
        {
            var jobKind = jobProgress.Item1;
            switch(jobKind)
            {
                case JobKind.Statistic:
                case JobKind.Compress:
                case JobKind.Backup:
                    _cancellationSignal = jobProgress.Item3;
                    this.BeginInvoke((() =>
                    {
                        _jobProgressIndicator.Value = jobProgress.Item2;
                        _jobProgressIndicator.Visible = true;
                        _jobIndicatorLabel.Text = GetJobIndicatorText(jobKind);
                        _jobIndicatorLabel.Visible = true;
                        _jobIndicatorLabel.Invalidate();
                    }));
                    break;
                case JobKind.Cleanup:
                    break;
            }
            this.BeginInvoke(() =>
            {
                startDataJobToolStripMenuItem.Enabled =
                    startStatisticJobToolStripMenuItem.Enabled = false;
            });
        }

        private void OnJobStarted(Tuple<JobKind, CancellationTokenSource> jobStartedEventArgs)
        {
            this.BeginInvoke(() =>
            {
                startDataJobToolStripMenuItem.Enabled =
                    startStatisticJobToolStripMenuItem.Enabled = false;
            });
        }

        private static string GetJobIndicatorText(JobKind jobKind)
        {
            switch(jobKind)
            {
                case JobKind.Statistic:
                    return "正在执行数据统计任务…";
                case JobKind.Compress:
                    return "正在执行数据压缩任务…";
                case JobKind.Backup:
                    return "正在执行数据备份任务…";
                case JobKind.Cleanup:
                    return "正在执行过期数据清理任务…";
            }
            return string.Empty;
        }

        private void OnJobEnded(JobKind jobKind)
        {
            this.BeginInvoke(() =>
            {
                _cancellationSignal = null;
                _jobProgressIndicator.Visible = false;
                _jobIndicatorLabel.Visible = false;
            });

            this.BeginInvoke(() =>
            {
                startDataJobToolStripMenuItem.Enabled =
                    startStatisticJobToolStripMenuItem.Enabled = true;
            });
        }

        private void OnBackupProgressIndicatorClick(object sender, EventArgs e)
        {
            ShowCancelBackupContextMenu();
        }

        private void OnBackupIndicatorLabelClick(object sender, EventArgs e)
        {
            ShowCancelBackupContextMenu();
        }

        private void ShowCancelBackupContextMenu()
        {
            _jobContextMenu.Show(this, PointToClient(Cursor.Position));
        }

        #endregion

        private void OnNotification(string notification)
        {
            this.BeginInvoke(() => _notificationToolStripStatusLabel.Text = notification);
            _notificationTimer.Change(5000, -1);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            BringToFront();
        }

        #region Alert Check & Handle

        private bool _hasAlert;

        private void OnAlert(Alert alert)
        {
            this.BeginInvoke(() =>
            {
                if(alert.AlertType == AlertType.Alert && !_hasAlert)
                {
                    _hasAlert = true;
                    _alertIndicator.Image = Resources.red_16;
                }
                if(alert is NoDataAlert && !_hasNoDataAlert)
                {
                    _hasNoDataAlert = true;
                    _dataIndicator.Image = Resources.red_16;
                }
            });
            OnNotification(alert.AlertContent);
        }

        private void OnNewMinute(DateTime obj)
        {
            this.BeginInvoke(() =>
            {
                if(_hasAlert &&
                    (DateTime.Now - AlertEvent.Instance.LastPublishTime).TotalHours > 1 &&
                        (DateTime.Now - LogOnlyAlertEvent.Instance.LastPublishTime).TotalHours > 1)
                {
                    // 一小时无报警，恢复正常状态
                    _hasAlert = false;
                    _alertIndicator.Image = Resources.green_16;
                }

                if(_hasNoDataAlert &&
                    (DateTime.Now - RealtimeDataMonitor.Instance.LastAlertTime).TotalSeconds >
                        Config.Instance.DataReadConfig.NoRealtimeDataAlertDurationSeconds)
                {
                    // 超过无数据报警时长而没有报警，解除无数据报警状态
                    _hasNoDataAlert = false;
                    _dataIndicator.Image = Resources.green_16;
                }
            });
        }

        #endregion

        #region View Related

        private TView RegisterView<TView>(TView view)
            where TView : BaseView
        {
            view.ViewChangeRequested += (sender, e) => ChangeView(e.Data);
            return view;
        }

        private void DoChangeView(BaseView view)
        {
            view.Dock = DockStyle.Fill;
            _clientPanel.Controls.Clear();
            _clientPanel.Controls.Add(view);
        }

        private void ChangeView(ViewKind view)
        {
            switch(view)
            {
                case ViewKind.Map:
                    DoChangeView(_mapView.Value);
                    break;
                case ViewKind.Deploy:
                    DoChangeView(_deployView.Value);
                    break;
                case ViewKind.Chart:
                    DoChangeView(_chartView.Value);
                    break;
                case ViewKind.Trend:
                    DoChangeView(_trendView.Value);
                    break;
                default:
                    throw new ArgumentOutOfRangeException("view");
            }
        }

        #endregion

        #region Menu Handlers

        private void mapViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeView(ViewKind.Map);
        }

        private void deployViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeView(ViewKind.Deploy);
        }

        private void chartViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeView(ViewKind.Chart);
        }

        private void trendViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeView(ViewKind.Trend);
        }

        private void OnCancelJobToolStripMenuItemClick(object sender, EventArgs e)
        {
            if(_cancellationSignal != null)
            {
                try
                {
                    _cancellationSignal.Cancel();
                }
                catch (Exception exception)
                {
                    AlertEvent.Instance.Publish(new ApplicationAlert(exception, "手动取消任务"));
                }
            }
        }

        private void historyAlertsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var alertHistoryWindow = new AlertHistoryWindow();
            alertHistoryWindow.Show();
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if(PasswordCorrect())
            {
                var settingsWindow = new SettingsWindow();
                settingsWindow.ShowDialog();
            }
        }

        private void startBackupJobToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManuallyStartAllMaintananceJob();
        }

        private void startStatisticJobToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManuallyStartStatJob();
        }

        #endregion

        private void _removableDiskSpaceIndicator_Click(object sender, EventArgs e)
        {
            removableDiskContextMenuStrip.Items.Clear();
            var removableDrive = RemovableDriveManager.Instance.Drive;
            if(removableDrive != null)
            {
                removableDiskContextMenuStrip.Items.Add(new ToolStripMenuItem(
                    string.Format("{0}", removableDrive)));
            }
            else
            {
                removableDiskContextMenuStrip.Items.Add(new ToolStripMenuItem(
                    "没有移动硬盘"));
            }
            removableDiskContextMenuStrip.Show(this, PointToClient(Cursor.Position));
        }
    }
}